import * as Cesium from '@cesiumjs';
import createGroundRectangle from "../../../utils/createGroundRectangle"

let JulianDate = Cesium.JulianDate;

// groundImages
/**
 * GroundImage用来创建贴地图像，并且图像可以轮换
 * @memberof Obj
 * @class
 * @extends XbsjCzmObj
 */

const $defaultOptions = {
  position: [0, 0, 0],
  width: 800,
  height: 600,
  autoWidth: false,
  autoHeight: true,
  origin: [0.5, 0.5],
  rotation: 0.0,
  textureWidth: 1024,
  textureHeight: 1024,
  show: true,
  // ground: true,
  urls: [], // imageUrls
  timeDuration: 1000, // 单位秒
  currentTime: 1,
  loopPlay: true,
  times: [],
  opacity: .50
};

var whiteImageBuffer = new Uint8Array([255, 255, 255, 0]);

const whiteImage = {
  width: 1,
  height: 1,
  arrayBufferView: whiteImageBuffer
};

class GroundImages {
  constructor(viewer, options, dataSources) {

    this.ready = false;

    console.log("GroundImages constructor")

    this.viewer = viewer;
    this.options = {
      ...$defaultOptions,
      ...options
    };

    this.init();

    this.dataSources = dataSources
  }

  init() {
    this._leftImage = undefined;
    this._rightImage = undefined;
    this.initImages();
    this.setupEventListener();
  }

  initImages() {
    this.initMaterial()
    this.updateMaterialSize()
    this.updateGroundPrimitive()
    this.updateOpacity()
    // this.addRectangleImage() //text
  }

  // defineProperties
  get scene() {
    return this.viewer.scene;
  }
  get clock() {
    return this.viewer.clock;
  }
  get dataSources() {
    return this._dataSources
  }
  set dataSources(val) {
    this._dataSources = val||[]
    this.updateSource()
  }
  get opacity() {
    return this.options.opacity
  }
  set opacity(val) {
    val = Math.max(val, 0)
    val = Math.min(val, 1)
    this.options.opacity = val
    this.updateOpacity()
    // this.imageChanged()
  }
  get currentTime() {
    return this.options.currentTime
  }
  set currentTime(val) {
    this.options.currentTime = val
    this.imageChanged()
  }
  get timeDuration() {
    return this.options._timeDuration
  }
  // set timeDuration(val) {
  //   val = Math.max(val, 0)
  //   this.options.timeDuration = val
  //   this.imageChanged()
  // }
  // defineProperties end

  updateSource() {

    this.loadImages()

    const {options, dataSources} = this;

    options._timeStart = JulianDate.fromIso8601(options.timeStart, new JulianDate())
    options._timeEnd = JulianDate.fromIso8601(options.timeEnd, new JulianDate())
    options._timeDuration = JulianDate.secondsDifference(options._timeEnd, options._timeStart)

    const { _timeStart, timeStart, timeEnd } = options;

    dataSources.unshift({time:timeStart, image: whiteImage})
    dataSources.push({time:timeEnd, image: whiteImage})

    dataSources.sort((a,b)=>{
      if(a>b) return 1;
      if(a<b) return -1;
      return 0
    })

    dataSources.forEach((e)=>{
      if(e.time){
        const time = JulianDate.fromIso8601(e.time, new JulianDate())
        e.timeDiff = JulianDate.secondsDifference(time, _timeStart)
      }
    })
  }

  selectImages(date){
    const {options, dataSources} = this;
    const { _timeStart, _timeDuration } = options;

    const timeDiff = JulianDate.secondsDifference(date, _timeStart)
    if(timeDiff<0) return [undefined,dataSources[0]];

    const n=this.dataSources.length
    for(let i=0;i<n; ++i){
      const data1 = dataSources[i];
      if(i+1==n){
        return [data1,undefined]
      }
      if(timeDiff==data1.timeDiff){
        return [data1,data1];
      }
      const data2 = dataSources[i+1];
      if(timeDiff<data2.timeDiff){
        const r = (timeDiff-data1.timeDiff)/(data2.timeDiff-data1.timeDiff)
        return [data1,data2, r];
      }
    }
    return [dataSources[n-1],dataSources[n-1]];
  }

  loadImages() {
    var _this = this;
    var promises = this.dataSources.map((e) => {
      return new Promise(function(resolve, reject) {
        if(!e.url){
          e.image = whiteImage;
          resolve(whiteImage);
          return;
        }
        Cesium.Resource.fetchImage({
          url: e.url
        }).then(function(image) {
          e.image = image;
          resolve(image);
        });
        // 这样写也可以
        // var img = new Image();
        // img.src = url;
        // img.onload = () => {
        //   resolve(img);
        // };
      });
    })
    Promise.all(promises)
      .then((result) => {
        console.log('影像加载完成');
        // _this.imageChanged()
        _this.ready = true;
      })
  }

  imageChanged() {

    if(!this.ready) return;

    var _this = this;
    const {options:opt, dataSources, currentTime} = this;
    const {textureWidth,textureHeight} = opt;

    _this._leftImage = undefined;
    _this._rightImage = undefined;

    const materials = _this._material.materials;
    const [ds1, ds2, ratio] = _this.selectImages(currentTime);
    _this._material.uniforms.t = ratio;

    var numImg = dataSources.length;
    if (!ds1) {
      const uniforms =  materials.left.uniforms;
      uniforms.image.copyFrom({source: whiteImage});
      uniforms.stscale = new Cesium.Cartesian2(1 / textureWidth, 1 / textureHeight);
    }
    if (!ds2) {
      const uniforms =  materials.right.uniforms;
      uniforms.image.copyFrom({source: whiteImage});
      uniforms.stscale = new Cesium.Cartesian2(1 / textureWidth, 1 / textureHeight);
    }

    if(ds1){
      const _image = ds1.image;
      _this._leftImage = _image;
      const uniforms =  materials.left.uniforms;
      uniforms.image.copyFrom({source: _image});
      uniforms.stscale = new Cesium.Cartesian2(_image.width / textureWidth, _image.height / textureHeight);
    }
    if(ds2){
      const _image = ds2.image;
      _this._rightImage = _image;
      const uniforms =  materials.right.uniforms;
      uniforms.image.copyFrom({source: _image});
      uniforms.stscale = new Cesium.Cartesian2(_image.width / textureWidth, _image.height / textureHeight);
    }
  }

  // test
  addRectangleImage() {
    var rectangle = this.scene.primitives.add(
      new Cesium.GroundPrimitive({
        geometryInstances: new Cesium.GeometryInstance({
          geometry: new Cesium.RectangleGeometry({
            rectangle: Cesium.Rectangle.fromDegrees(80.0, 30.0, 160.0, 50.0),
            vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT
          })
        }),
        appearance: new Cesium.EllipsoidSurfaceAppearance({
          aboveGround: false
        })
      })
    );
    rectangle.appearance.material = new Cesium.Material({
      fabric: {
        type: 'Image',
        uniforms: {
          image: '//zzgis.com/cdn/3d/Sandcastle/images/Cesium_Logo_Color.jpg'
        }
      }
    });
  }

  initMaterial() {
    const _material = new Cesium.Material({
      fabric: {
        type: 'ImageMapXC',
        materials: {
          left: {
            type: 'ScaleImageXC',
            uniforms: {
              image: Cesium.Material.DefaultImageId,
              repeat: new Cesium.Cartesian2(1.0, 1.0),
              color: new Cesium.Color(1.0, 1.0, 1.0, 1.0),
              stscale: new Cesium.Cartesian2(1.0, 1.0)
            },
            components: {
              diffuse: 'texture2D(image, fract(repeat * materialInput.st) * stscale).rgb * color.rgb',
              alpha: 'texture2D(image, fract(repeat * materialInput.st) * stscale).a * color.a'
            }
          },
          right: {
            type: 'ScaleImageXC',
            uniforms: {
              image: Cesium.Material.DefaultImageId,
              repeat: new Cesium.Cartesian2(1.0, 1.0),
              color: new Cesium.Color(1.0, 1.0, 1.0, 1.0),
              stscale: new Cesium.Cartesian2(1.0, 1.0)
            },
            components: {
              diffuse: 'texture2D(image, fract(repeat * materialInput.st) * stscale).rgb * color.rgb',
              alpha: 'texture2D(image, fract(repeat * materialInput.st) * stscale).a * color.a'
            }
          }
        },
        uniforms: {
          t: 0.0,
          opacity: 1.0
        },
        components: {
          diffuse: 'left.diffuse * (1.0 - t) + right.diffuse * t',
          alpha: '(left.alpha * (1.0 - t) + right.alpha * t) * opacity'
        }
      }
    });

    this._material = _material;
  }

  updateMaterialSize() {
    const {
      scene,
      options: {
        textureWidth,
        textureHeight
      }
    } = this;

    this._leftHeatmapTexture && this._leftHeatmapTexture.destroy();

    this._leftHeatmapTexture = new Cesium.Texture({
      context: scene.context,
      width: textureWidth,
      height: textureHeight
    });

    this._rightHeatmapTexture && this._rightHeatmapTexture.destroy();

    this._rightHeatmapTexture = new Cesium.Texture({
      context: scene.context,
      width: textureWidth,
      height: textureHeight
    });

    this._material.materials.left.uniforms.image = this._leftHeatmapTexture;
    this._material.materials.right.uniforms.image = this._rightHeatmapTexture;
    // this._material.materials.left.uniforms.color = new Cesium.Color(1, 0, 0, 0.5); //test
    // this._material.materials.right.uniforms.color = new Cesium.Color(0, 0, 1, 0.5); //test
  };

  updateGroundPrimitive() {
    const _this = this;
    const {
      scene,
      options,
      _material
    } = _this;

    _this._groundPrimitive && scene.groundPrimitives.remove(_this._groundPrimitive);
    _this._groundPrimitive = undefined;

    const {
      show,
      position,
      origin:[ox,oy],
      rotation,
      width,
      height,
      textureWidth,
      textureHeight
    } = options || {};

    Cesium.GroundPrimitive.initializeTerrainHeights().then(() => {
      var groundImage = createGroundRectangle(position, width, height, ox, oy, rotation, undefined);
      // var groundImage = createGroundRectangle(position, textureWidth);
      _this._groundPrimitive = scene.groundPrimitives.add(groundImage);
      _this._groundPrimitive.show = show; // 避免groundImage创建时设置show属性不起作用
      _this._groundPrimitive.appearance.material = _material;
    })
  };

  updateOpacity() {
    this._material.uniforms.opacity = this.opacity;
  };

  flyTo(options) {
    const {
      duration,
      complete
    } = (options || {})

    const {
      position,
      width,
    } = this.options;

    const [x, y, z] = position;
    const destination = Cesium.Cartesian3.fromRadians(x, y, z);
    // this.viewer.camera.flyTo(position, width, [0, -Math.PI / 3, 0]);
    this.viewer.scene.camera.flyTo({
      destination,
      duration,
      complete,
      orientation: {
        heading: 0,
        pitch: -1.57,
        roll: 0,
      }
    });
  }

  setupEventListener() {
    const _this = this;
    const clock = this.viewer.clock;
    _this._eventListener =
      _this.viewer.scene.preUpdate.addEventListener(() => {
        _this.currentTime = clock.currentTime
      });
  }

  destroy() {
    this._eventListener && this.viewer.scene.preUpdate.removeEventListener(this._eventListener);
    this._eventListener = null
  }
};

export default GroundImages;
